#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>

#include "sboxes.h"


char* int2bits(int i) {
	switch(i) {
		case 0: return  "0000";
		case 1: return  "0001";
		case 2: return  "0010";
		case 3: return  "0011";
		case 4: return  "0100";
		case 5: return  "0101";
		case 6: return  "0110";
		case 7: return  "0111";
		case 8: return  "1000";
		case 9: return  "1001";
		case 10: return "1010";
		case 11: return "1011";
		case 12: return "1100";
		case 13: return "1101";
		case 14: return "1110";
		case 15: return "1111";
		default: return NULL;
	}	
	return NULL;
}

int getRow(char *c, int index) { // 0 <= index < 8
	char p,d; // first & last bit
	p=c[index];
	d=c[index+5];
	return 2*(p-'0')+(d-'0');
}

int getColumn(char *c, int index) {
	char b1,b2,b3,b4;
	b1=c[index+1];
	b2=c[index+2];
	b3=c[index+3];
	b4=c[index+4];
	return 8*(b1-'0')+4*(b2-'0')+2*(b3-'0')+1*(b4-'0');
}

int bin2int(char *filename) {
  int res;
  char *tmp;
  tmp=(char *)malloc(6*sizeof(*tmp));
  strncpy(tmp, filename+strlen(filename)-10, 6);

  res=32*(tmp[0]-'0')
    +16*(tmp[1]-'0')
    +8*(tmp[2]-'0')
    +4*(tmp[3]-'0')
    +2*(tmp[4]-'0')
    +1*(tmp[5]-'0')
    ;

  free(tmp);

  return res;
}

/*
// Poids de hamming de la chaine sz

int hamming(char *sz, int stop) {
	int i;
	int res;
	char *tmp;
	
	res=0;
	tmp=sz;
	for(i=0;i<stop;++i) {
		if(*sz=='1') res++;
		sz++;
	}	
	
	return res;
}
*/


int main(int argc, char **argv) {
	FILE *key;
	FILE *msg;
	char c;
	char d;
	char e;
	char f;
	char tmp;
	char k[64];
	char m[64];
	char m2[64];
	char L[32];
	char R[32];
	char RE[48]; // E(R), 48 bits
	char KP[64-8];
	char K1[16][48]; // subkeys
	char ExorK[48]; // ExorK = E^K, 48 bits
	char res[33]; // 32bits
	char P[33];
	char LR[64]; //pre output
	char cipher[64]; //output
	char filename_key[64];
	char filename_msg[64];
	int i;
	int j;
	int z;
	int l;
	int rd;
	//int a1,a2;
	int index_k,index_m;
	int n0,n1;
	//int Tab_k_m[4][64][64]; // tab_k_m[attacked_bit][key][msg]
	int round;
  
	if(argc<=2) {
		printf("usage:\t./des <nbrondes> -k [<file.key>]+ -m [<file.msg>]+\n");
		return 0;	
	}
	
	//sbox=atoi(argv[1]);
	round=atoi(argv[1]);
	index_k=0;
	index_m=0;
	
	for(i=2;i<argc;++i) { // we want key = argv[index_k], msg = argv[index_m]
		if(strcmp(argv[i], "-k")==0) {
			index_k=i+1;
		} else if(strcmp(argv[i], "-m")==0) {
			index_m=i+1;
		}
	}
	
	/*for(i=0;i<4;++i) { //initialistaion of Tab_k_m
		for (a1=0; a1<64; a1++) {
			for (a2=0; a2<64; a2++) {
				Tab_k_m[i][a1][a2]=42;
			}
		}
	}*/

	n0=0;
	n1=0;
	// We may get filenames with spaces!
	for(z=index_k;z<=index_m-2;++z) { // k		
		//strcpy(filename_key, argv[z]);
		
		for(l=index_m;l<argc;++l) { // m
		
			strcpy(filename_key, argv[z]);
			strcpy(filename_msg, argv[l]);

//printf("filename_key=%s\n", filename_key);
//printf("filename_msg=%s\n", filename_msg);	
			
			for(i=0;i<64;++i) { //initialisation of m and k
				m[i]=0;
				k[i]=0;
			}
		  
			msg=fopen(filename_msg, "r");
			if(msg==NULL){
				printf("Probleme dans l'ouverture du fichier %s\n",filename_msg);
				return 0;
			}
		  	fscanf(msg, "%s\n", m);		  	
	  	
//printf("m:\t");
//for(i=0;i<64;++i) printf("%c", m[i]);
//printf("\n");		  	
				
			strncpy(m2, m, 64);
			
			
			// get the key
			key=fopen(filename_key, "r");
			if(key==NULL){
				printf("Probleme dans l'ouverture du fichier %s\n",filename_key);
				return 0;
			}
			fscanf(key, "%s\n", k);
						
			// Initial permutation (IP)
			//for(i=0;i<64;++i) m[i]=m2[64-IP[1+i]-1]; A L'ENVERS
			for(i=0;i<64;++i) m[i]=m2[IP[1+i]-1];			

			

			strncpy(L, m, 32);
			strncpy(R, m+32, 32);


			

			// Key schedule
		for(i=1,j=0;i<=64;++i) {
				if(i%8!=0) {
					KP[j]=k[PC1[i]-1];
					j++;
				}
			}

		for(rd=0;rd<16;rd++){
			
			
			if( (rd == 0) || (rd == 1) ||(rd == 8) ||(rd == 15) ){
				c=KP[0];
				d=KP[28];
		   		for(i=0;i<=54;++i) KP[i] = KP[i+1];
				KP[55] = d;
		    		KP[27] = c;
			}
			else{
				c=KP[0];
				d=KP[1];
				e=KP[28];
				f=KP[29];
		   		for(i=0;i<=54;++i) KP[i] = KP[i+2];
				KP[54] = e;
				KP[55] = f;
		    		KP[26] = c;
				KP[27] = d;
			}
		   
			for(i=0;i<48;++i) K1[rd][i]=KP[PC2[i+1]-1];
		  
		  // End KeySchedule!
//printf("K1[%d]:\t",rd+1);
//for(i=0;i<48;++i) printf("%c", K1[rd][i]);
//printf("\n");
		}



			
// rondes
			for(rd=0;rd<round;rd++){
				// Key Expansion
				for(i=0;i<48;++i) RE[i]=R[Expand[i+1]-1];
			
//printf("RE:\t");
//for(i=0;i<48;++i) printf("%c", RE[i]);
//printf("\n");
			
			//for(i=1;i<=64;++i) m[i]=m2[IP[i]];
			//for(i=0;i<48;++i) ExorK[i]='0'+(RE[i]^k[i]);
			

//printf("k:\t");
//for(i=0;i<64;++i) printf("%c", k[i]);
//printf("\n");
			
			
				// XOR with Subkey	
				for(i=0;i<48;++i) ExorK[i]='0'+(K1[rd][i]^RE[i]);
									
//printf("ExorK:\t");
//for(i=0;i<48;++i) printf("%c", ExorK[i]);
//printf("\n");
				// Passing in S-Boxes 	
				for(i=0;i<8;++i) {
					strncpy(res+4*i, int2bits(S[i][getRow(ExorK, 6*i)][getColumn(ExorK, 6*i)]), 4); 
				}
				res[32]='\0';
						
//printf("res:\t%s\n", res);
//printf("P:\t");
				for(i=0;i<32;i++){
					P[i] = res[Per[i]-1];
				//printf("%c", P[i]);
				}
				P[32]=0;

				//writing of blocs L and R
				for(i=0;i<32;++i){
					tmp=L[i];
					L[i]=R[i];
					R[i]='0'+(tmp^P[i]);
				}
//printf("L:\t");
//for(i=0;i<32;++i) printf("%c", L[i]);
//printf("\n");

//printf("R:\t");
//for(i=0;i<32;++i) printf("%c", R[i]);
//printf("\n");

			}//end round
			

			//IP-1
			if(round==16){	
				for(i=0;i<32;i++){
					LR[i+32] = L[i];
					LR[i] = R[i];
				}
//printf("\nLR:\t");
//for(i=0;i<64;++i) printf("%c",LR[i]);
//printf("\n");
		
				for(i=0;i<64;i++){
				cipher[i] = LR[IP_inv[i+1]-1];
				}

//printf("cipher:\t");
for(i=0;i<64;++i) printf("%c",cipher[i]);
printf("\n");
			}
			fclose(key);
			fclose(msg);
			
		} //for(i=index_m;i<argc;++i)
	} //for(i=index_k;i<=index_m-2;++i)
	

	return 0;	
}
